home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / X11 / xconq / draw.c < prev    next >
C/C++ Source or Header  |  1995-05-09  |  17KB  |  552 lines

  1. /* Copyright (c) 1987, 1988  Stanley T. Shebs, University of Utah. */
  2. /* This program may be used, copied, modified, and redistributed freely */
  3. /* for noncommercial purposes, so long as this notice remains intact. */
  4.  
  5. #pragma comment(exestr, "@(#) draw.c 12.1 95/05/09 ")
  6.  
  7. /* RCS $Header: draw.c,v 1.3 88/07/18 17:10:44 shebs Exp $ */
  8.  
  9. /* Geometry is somewhat tricky because our viewports are supposed */
  10. /* to wrap around a cylinder transparently.  The general idea is that */
  11. /* if modulo opns map x coordinates onto a cylinder, adding and subtracting */
  12. /* the diameter of the cylinder "un-modulos" things.  If this doesn't make */
  13. /* any sense to you, then be careful about fiddling with the code! */
  14.  
  15. /* If that wasn't bad enough, the hexes constitute an oblique coordinate */
  16. /* where the axes form a 60 degree angle to each other.  Fortunately, no */
  17. /* trig is necessary - to convert to/from rectangular, add/subtract 1/2 of */
  18. /* the y coordinate to x, and leave the y coordinate alone. */
  19.  
  20. /* The graphical code uses mostly text drawing functions, which are more */
  21. /* likely to be efficient than is random bitblting.  (The interface may */
  22. /* implement the operations as random blitting, but that's OK.) */
  23.  
  24. #include "config.h"
  25. #include "misc.h"
  26. #include "period.h"
  27. #include "side.h"
  28. #include "unit.h"
  29. #include "map.h"
  30.  
  31. extern bool populations;        /* used to decide about running pop display */
  32.  
  33. char rowbuf[BUFSIZE];           /* buffer for terrain row drawing */
  34.  
  35. /* redraw() has moved to the display code in X11.c and curses.c */
  36.  
  37. /* Ensure that given location is visible.  We also flush the input because */
  38. /* any input relating to a different screen is probably worthless. */
  39.  
  40. put_on_screen(side, x, y)
  41. Side *side;
  42. int x, y;
  43. {
  44.     /* Ugly hack to prevent extra boxes being drawn during init - don't ask!*/
  45.     if (x == 0 && y == 0) return;
  46.     if (active_display(side)) {
  47.     if (!in_middle(side, x, y)) {
  48.         side->vcx = wrap(x);
  49.         side->vcy = min(max(side->vh2-(1-(side->vh&1)), y),
  50.                 (world.height-1)-side->vh2);
  51.         if (side->lastvcx >= 0) undraw_box(side);
  52.         show_map(side);
  53.         if (side->lastvcx >= 0) draw_box(side);
  54.         flush_output(side);
  55.             flush_input(side);
  56.     }
  57.     }
  58. }
  59.  
  60. /* Decide whether given location is not too close to edge of screen. */
  61. /* We do this because it's a pain to move units when half the adjacent */
  62. /* places aren't even visible.  This routine effectively places a lower */
  63. /* limit of 5x5 for the map window. (I think) */
  64.  
  65. in_middle(side, x, y)
  66. Side *side;
  67. int x, y;
  68. {
  69.     int vcx = side->vcx, vcy = side->vcy;
  70.     int vw2 = side->vw2, vh2 = side->vh2;
  71.     int top, bot;
  72.  
  73.     /* handle the boundary condition near top and bottom */
  74.     bot = vcy - vh2 + 2;
  75.     if (bot == 2) bot = 1;
  76.     top = vcy + vh2 - 2;
  77.     if (top == world.height-3) top = world.height - 2;
  78.     if (!between(bot, y, top))
  79.     return FALSE;
  80.     x = unwrap(side, x + (y - vcy) / 2);
  81.     return between(vcx-vw2+2, x, vcx+vw2-3+(side->vw&1));
  82. }
  83.  
  84. /* Transform map coordinates into screen coordinates, relative to the given */
  85. /* side.  Allow for cylindricalness and number of pixels in a hex. */
  86.  
  87. xform(side, x, y, sxp, syp)
  88. Side *side;
  89. int x, y, *sxp, *syp;
  90. {
  91.     *sxp = ((side->hw * (x - (side->vcx - side->vw2))) +
  92.         (side->hw * (y - side->vcy)) / 2);
  93.     *syp = side->hch * ((side->vcy + side->vh2) - y);
  94. }
  95.  
  96. /* Undo the wrapping effect, relative to viewport location. */
  97. /* Note that both conditions cannot both be true at the same time, */
  98. /* since viewport is smaller than map. */
  99.  
  100. unwrap(side, x)
  101. Side *side;
  102. int x;
  103. {
  104.     int vcx = side->vcx, vw2 = side->vw2, vw34 = side->vw2 + (side->vw2 >> 1);
  105.  
  106.     if (vcx - vw2 < 0 && x > vcx + vw34) x -= world.width;
  107.     if (vcx + vw2 > world.width-1 && x < vcx - vw34) x += world.width;
  108.     return x;
  109. }
  110.  
  111. /* Un-transform screen coordinates (as supplied by mouse perhaps) into */
  112. /* map coordinates.  This doesn't actually account for the details of */
  113. /* hexagonal boundaries, and actually discriminates box-shaped areas. */
  114.  
  115. deform(side, sx, sy, xp, yp)
  116. Side *side;
  117. int sx, sy, *xp, *yp;
  118. {
  119.     int vcx = side->vcx, vcy = side->vcy, adjust;
  120.  
  121.     *yp = (vcy + side->vh2) - (sy / side->hch);
  122.     adjust = (((*yp - vcy) & 1) ? ((side->hw/2) * (*yp >= vcy ? 1 : -1)) : 0);
  123.     *xp = wrap(((sx - adjust) /    side->hw) - (*yp - vcy) / 2 +
  124.            (vcx - side->vw2));
  125. }
  126.  
  127. /* Transform coordinates in the world display.  Simpler, since no moving */
  128. /* viewport nonsense. */
  129.  
  130. w_xform(side, x, y, sxp, syp)
  131. Side *side;
  132. int x, y, *sxp, *syp;
  133. {
  134.     *sxp = side->mm * x + (side->mm * y) / 2;
  135.     *syp = side->mm * (world.height - 1 - y);
  136. }
  137.  
  138. /* Redraw the map of the whole world.  We use square blobs instead of icons, */
  139. /* since individual "hexes" may be as little as 1x1 pixels in size, and */
  140. /* there are lots of them.  Algorithm uses run-length encoding to find and */
  141. /* draw bars of constant color.  Monochrome just draws units - no good */
  142. /* choices for terrain display. */
  143.  
  144. show_world(side)
  145. Side *side;
  146. {
  147.     int x, y, color, barcolor, x1;
  148.  
  149.     if (active_display(side) && world_display(side)) {
  150.     clear_window(side, side->world);
  151.     for (y = world.height-1; y >= 0; --y) {
  152.         x1 = 0;
  153.         barcolor = world_color(side, x1, y);
  154.         for (x = 0; x < world.width; ++x) {
  155.         color = world_color(side, x, y);
  156.         if (color != barcolor) {
  157.             draw_bar(side, x1, y, x - x1, barcolor);
  158.             x1 = x;
  159.             barcolor = color;
  160.         }
  161.         }
  162.         draw_bar(side, x1, y, world.width - x1, barcolor);
  163.     }
  164.     if (side->vcy >= 0) draw_box(side);
  165.     }
  166. }
  167.  
  168. /* Compute the color representing a hex from the given side's point of view. */
  169.  
  170. world_color(side, x, y)
  171. Side *side;
  172. int x, y;
  173. {
  174.     int view = side_view(side, x, y);
  175.     Side *side2;
  176.  
  177.     if (side->monochrome) {
  178.     return ((view == UNSEEN || view == EMPTY) ? side->bgcolor :
  179.                                             side->fgcolor);
  180.     } else {
  181.     if (view == UNSEEN) {
  182.         return (side->bgcolor);
  183.     } else if (view == EMPTY) {
  184.         return (side->hexcolor[terrain_at(x, y)]);
  185.     } else {
  186.         side2 = side_n(vside(view));
  187.         return ((side2 == NULL) ? side->neutcolor :
  188.             (allied_side(side2, side) ? side->altcolor :
  189.              side->enemycolor));
  190.     }
  191.     }
  192. }
  193.  
  194. /* Draw an outline box on the world map.  Since we adopt the dubious trick */
  195. /* of inverting through all planes, must be careful to undo before moving; */
  196. /* also, draw/undraw shiftedly so both boxes appear on both sides of world. */
  197.  
  198. draw_box(side)
  199. Side *side;
  200. {
  201.     invert_box(side, side->vcx, side->vcy);
  202.     invert_box(side, side->vcx - world.width, side->vcy);
  203.     side->lastvcx = side->vcx;  side->lastvcy = side->vcy;
  204. }
  205.  
  206. undraw_box(side)
  207. Side *side;
  208. {
  209.     invert_box(side, side->lastvcx, side->lastvcy);
  210.     invert_box(side, side->lastvcx - world.width, side->lastvcy);
  211. }
  212.  
  213. /* Draw immediate area in more detail.  We make a little effort to avoid */
  214. /* drawing hexes off the visible part of the screen, but are still somewhat */
  215. /* conservative, so as not to get holes in the display.  Implication is that */
  216. /* some lower level of routines has to be able to clip the map window. */
  217.  
  218. show_map(side)
  219. Side *side;
  220. {
  221.     int y1, y2, y, x1, x2, adj;
  222.  
  223.     if (active_display(side)) {
  224.     clear_window(side, side->map);
  225.     y1 = side->vcy + side->vh2;
  226.     y2 = side->vcy - side->vh2 + 1 - (side->vh & 1);
  227.     for (y = y1; y >= y2; --y) {
  228.         adj = (y - side->vcy) / 2;
  229.         x1 = side->vcx - side->vw2 - adj - 1;
  230.         x2 = side->vcx + side->vw2 - adj + 1 + (side->vw & 1);
  231.         draw_row(side, x1, y, x2 - x1);
  232.     }
  233.     draw_cursor(side);
  234.     flush_output(side);
  235. /*    draw_box(side);  /* must be after flush */
  236.     }
  237. }
  238.  
  239. /* Draw an individual detailed hex, as a row of one. */
  240. /* This routine may be called in cases where the hex is not on the main */
  241. /* screen;  if so, then the world map but not the local map is drawn on. */
  242. /* (should the display be shifted to make visible?) */
  243.  
  244. draw_hex(side, x, y, flushit)
  245. Side *side;
  246. int x, y;
  247. bool flushit;
  248. {
  249.     int sx, sy;
  250.  
  251.     if (active_display(side)) {
  252.     if (side->monochrome || side->showmode == TERRICONS) {
  253.         xform(side, unwrap(side, x), y, &sx, &sy);
  254.         draw_hex_icon(side, side->map, sx, sy, side->bgcolor, HEX);
  255.     }
  256.     draw_row(side, unwrap(side, x), y, 1);
  257.     draw_bar(side, x, y, 1, world_color(side, x, y));
  258.     if (flushit) flush_output(side);
  259.     }
  260. }
  261.  
  262. /* The basic map drawing routine does an entire row at a time, which yields */
  263. /* order-of-magnitude speedups (!).  This routine is complicated by several */
  264. /* tricks:  1) in monochrome, the entire line can be drawn at once; 2) in */
  265. /* color, run-length encoding maximizes the length of constant-color strings */
  266. /* and 3) anything which is in the background color need not be drawn. */
  267. /* In general, this routine dominates the map viewing process, so efficiency */
  268. /* here is very important. */
  269.  
  270. draw_row(side, x0, y0, len)
  271. Side *side;
  272. int x0, y0, len;
  273. {
  274.     bool empty = TRUE;
  275.     char ch;
  276.     int i = 0, x, x1, color, segcolor, sx, sy;
  277.  
  278.     if (side->monochrome) {
  279.     xform(side, x0, y0, &sx, &sy);
  280.     for (x = x0; x < x0 + len; ++x) {
  281.         if (side_view(side, wrap(x), y0) == EMPTY) {
  282.         rowbuf[i++] = ttypes[terrain_at(wrap(x), y0)].tchar;
  283.         empty = FALSE;
  284.         } else {
  285.         rowbuf[i++] = ' ';
  286.         }
  287.     }
  288.     if (!empty) draw_terrain_row(side, sx, sy, rowbuf, i, side->fgcolor);
  289.     } else {
  290.     x1 = x0;
  291.     segcolor = hex_color(side, x0, y0);
  292.     for (x = x0; x < x0 + len; ++x) {
  293.         color = hex_color(side, x, y0);
  294.         if (color != segcolor) {
  295.         if (segcolor != side->bgcolor) {
  296.             xform(side, x1, y0, &sx, &sy);
  297.             draw_terrain_row(side, sx, sy, rowbuf, i, segcolor);
  298.         }
  299.         i = 0;
  300.         x1 = x;
  301.         segcolor = color;
  302.         }
  303.         switch(side->showmode) {
  304.         case FULLHEX:
  305.         case BOTHICONS:
  306.         ch = HEX;
  307.         break;
  308.         case BORDERHEX:
  309.         ch = OHEX;
  310.         break;
  311.         case TERRICONS:
  312.         ch = ttypes[terrain_at(wrap(x), y0)].tchar;
  313.         break;
  314.         }
  315.         rowbuf[i++] = ch;
  316.     }
  317.     if (len == 1) i = 1;
  318.     xform(side, x1, y0, &sx, &sy);
  319.     draw_terrain_row(side, sx, sy, rowbuf, i, segcolor);
  320.     if (side->showmode == BOTHICONS) {
  321.         i = 0;
  322.         x1 = x0;
  323.         segcolor = terricon_color(side, x0, y0);
  324.         for (x = x0; x < x0 + len; ++x) {
  325.         color = terricon_color(side, x, y0);
  326.         if (color != segcolor) {
  327.             xform(side, x1, y0, &sx, &sy);
  328.             draw_terrain_row(side, sx, sy, rowbuf, i, segcolor);
  329.             i = 0;
  330.             x1 = x;
  331.             segcolor = color;
  332.         }
  333.         rowbuf[i++] = ttypes[terrain_at(wrap(x), y0)].tchar;
  334.         }
  335.         if (len == 1) i = 1;
  336.         xform(side, x1, y0, &sx, &sy);
  337.         draw_terrain_row(side, sx, sy, rowbuf, i, segcolor);
  338.     }
  339.     }
  340.     /* Units are much harder to optimize - fortunately they're sparse */
  341.     for (x = x0; x < x0 + len; ++x) {
  342.     draw_unit(side, x, y0);
  343.     }
  344. }
  345.  
  346. /* Return the color of the hex. (for color displays only) */
  347.  
  348. hex_color(side, x, y)
  349. Side *side;
  350. int x, y;
  351. {
  352.     return ((side_view(side, wrap(x), y) == UNSEEN) ? side->bgcolor :
  353.         side->hexcolor[terrain_at(wrap(x), y)]);
  354. }
  355.  
  356. /* Return the color of a terrain icon overlaying a colored hex. */
  357.  
  358. terricon_color(side, x, y)
  359. Side *side;
  360. int x, y;
  361. {
  362.     return ((side_view(side, wrap(x), y) == UNSEEN) ? side->bgcolor :
  363.         (ttypes[terrain_at(wrap(x), y)].dark ? side->fgcolor :
  364.          side->bgcolor));
  365. }
  366.  
  367. /* Draw a single unit icon as appropriate.  This *also* has a bunch of */
  368. /* details to worry about: centering of icon in hex, clearing a rectangular */
  369. /* area for the icon, picking a color for the unit, using either a bitmap */
  370. /* or font char, and adding a side number for many-player games. */
  371. /* Must also be careful not to draw black-on-black for units in space. */
  372. /* This routine has also been drafted into drawing populace side numbers */
  373. /* for otherwise empty hexes. */
  374.  
  375. draw_unit(side, x, y)
  376. Side *side;
  377. int x, y;
  378. {
  379.     int view = side_view(side, wrap(x), y), sx, sy, ucolor, hcolor, n;
  380.     int terr = terrain_at(wrap(x), y), pop, pcolor;
  381.     Side *side2;
  382.  
  383.     if (view != UNSEEN) {
  384.     if (view == EMPTY) {
  385.         if (populations) {
  386.         pop = people_at(wrap(x), y);
  387.         if (pop != NOBODY) {
  388.             side2 = side_n(pop-8);
  389.             pcolor = (allied_side(side, side2) ? side->owncolor :
  390.                   (enemy_side(side, side2) ? side->enemycolor :
  391.                    side->neutcolor));
  392.             if (pcolor == side->owncolor && 
  393.             (ttypes[terr].dark ||
  394.              side->monochrome ||
  395.              (side->showmode == TERRICONS)))
  396.             pcolor = side->fgcolor;
  397.             xform(side, x, y, &sx, &sy);
  398.             draw_side_number(side, side->map, sx, sy, pop-8, pcolor);
  399.         }
  400.         }
  401.     } else {
  402.         xform(side, x, y, &sx, &sy);
  403.         side2 = side_n(vside(view));
  404.         ucolor = (allied_side(side, side2) ? side->owncolor :
  405.               (enemy_side(side, side2) ? side->enemycolor :
  406.                side->neutcolor));
  407.         if (ucolor == side->owncolor && 
  408.         (ttypes[terr].dark ||
  409.          side->monochrome ||
  410.          (side->showmode == TERRICONS)))
  411.         ucolor = side->fgcolor;
  412.         if (side->monochrome && side != side2)
  413.         ucolor = side->bgcolor;
  414.         hcolor = (side == side2 ? side->bgcolor : side->fgcolor);
  415.         if (side->monochrome) {
  416.         /* erasing background */
  417.         draw_hex_icon(side, side->map, sx, sy, hcolor, HEX);
  418.         } else if (side->showmode != TERRICONS) {
  419.         draw_hex_icon(side, side->map, sx, sy, hex_color(side, x, y),
  420.                   ((side->showmode == BORDERHEX) ? OHEX : HEX));
  421.         }
  422.         draw_unit_icon(side, side->map, sx, sy, vtype(view), ucolor);
  423.         n = side_number(side2);
  424.         if ((numsides > 2 || side->monochrome) && n != side_number(side)) {
  425.         draw_side_number(side, side->map, sx, sy, n, ucolor);
  426.         }
  427.     }
  428.     }
  429. }
  430.  
  431. /* Cursor drawing also draws the unit in some other color if it's not the */
  432. /* "top-level" unit in a hex, as well as getting the player's attention */
  433. /* if the new location is sufficiently far from the last. */
  434.  
  435. draw_cursor(side)
  436. Side *side;
  437. {
  438.     int sx, sy;
  439.  
  440.     if (active_display(side)) {
  441.     /* ugly hack to prevent extra cursor draw */
  442.     if (side->cury == 0) return;
  443.     xform(side, unwrap(side, side->curx), side->cury, &sx, &sy);
  444.     if (side->curunit != NULL && side->curunit->transport != NULL) {
  445.         if (side->monochrome) {
  446.         draw_hex_icon(side, side->map, sx, sy, side->bgcolor, HEX);
  447.         draw_unit_icon(side, side->map, sx, sy,
  448.                    side->curunit->type, side->fgcolor);
  449.         } else {
  450.         draw_unit_icon(side, side->map, sx, sy,
  451.                    side->curunit->type, side->diffcolor);
  452.         }
  453.     }
  454.     /* Flash something to draw the eye a long ways */
  455.     if (humanside(side)) {
  456.         if (distance(side->curx, side->cury, side->lastx, side->lasty) > 3)
  457.         flash_position(side, sx, sy, DELAY);
  458.         draw_cursor_icon(side, sx, sy);
  459.         side->lastx = side->curx;  side->lasty = side->cury;
  460.     }
  461.     }
  462. }
  463.  
  464. /* Get rid of cursor by redrawing the hex. */
  465.  
  466. erase_cursor(side)
  467. Side *side;
  468. {
  469.     if (side->lasty > 0) draw_hex(side, side->lastx, side->lasty, TRUE);
  470. }
  471.  
  472. int fix_mushroom_hex();
  473.  
  474. /* Draw a splat visible to both sides at a given location.  Several splats */
  475. /* available, depending on the seriousness of the hit.  Make an extra-flashy */
  476. /* display when The Bomb goes off.  Because of the time delays involved, we */
  477. /* have to update both sides' displays more or less simultaneously. Would be */
  478. /* better to exhibit to all sides maybe, but I'm not going to bother! */
  479.  
  480. draw_blast(unit, es, hit)
  481. Unit *unit;
  482. Side *es;
  483. int hit;
  484. {
  485.     char ch;
  486.     int ux = unit->x, uy = unit->y, sx, sy, i;
  487.     Side *us = unit->side;
  488.  
  489.     if (hit >= period.nukehit) {
  490.     if (active_display(us)) invert_whole_map(us);
  491.     if (active_display(es)) invert_whole_map(es);
  492.     /* may need a time delay if X is too speedy */
  493.     nap(500);
  494.     if (active_display(us)) invert_whole_map(us);
  495.     if (active_display(es)) invert_whole_map(es);
  496.     for (i = 0; i < 4; ++i) {
  497.         if (active_display(us)) draw_mushroom(us, ux, uy, i);
  498.         if (active_display(es)) draw_mushroom(es, ux, uy, i);
  499.         if (i != 2 && (active_display(us) || active_display(es))) sleep(1);
  500.     }
  501.     if (active_display(us) || active_display(es)) sleep(1);
  502.     tmpside = us;
  503.     apply_to_area(ux,uy,2,fix_mushroom_hex);
  504.     flush_output(us);
  505.     tmpside = es;
  506.     apply_to_area(ux,uy,2,fix_mushroom_hex);
  507.     flush_output(es);
  508.     } else {
  509.     ch = ((hit >= unit->hp) ? 'd' : ((hit > 0) ? 'c' : 'b'));
  510.     if (active_display(us)) {
  511.         xform(us, unwrap(us, ux), uy, &sx, &sy);
  512.         draw_blast_icon(us, us->map, sx, sy, ch, us->enemycolor);
  513.         flush_output(us);
  514.         nap(250);
  515.         draw_hex(us,ux,uy,TRUE);
  516.     }
  517.     if (active_display(es)) {
  518.         xform(es, unwrap(es, ux), uy, &sx, &sy);
  519.         draw_blast_icon(es, es->map, sx, sy, ch, es->owncolor);
  520.         flush_output(es);
  521.         nap(250);
  522.         draw_hex(es, ux, uy,TRUE);
  523.     }
  524.     }
  525. }
  526.  
  527. /* Draw all the units in a column.  They should be spaced so they don't */
  528. /* overlap or get misaligned with text, and can be inverted if desired. */
  529.  
  530. draw_unit_list(side, hilite)
  531. Side *side;
  532. bool hilite[];
  533. {
  534.     int u, pos = 0, spacing = max(side->hh, side->fh);
  535.     
  536.     if (active_display(side)) {
  537.     for_all_unit_types(u) {
  538.         draw_hex_icon(side, side->state, side->margin, pos,
  539.               (hilite[u] ? side->fgcolor : side->bgcolor), OHEX);
  540.         draw_unit_icon(side, side->state, side->margin, pos, u,
  541.                (hilite[u] ? side->bgcolor : side->fgcolor));
  542.         pos += spacing;
  543.     }
  544.     }
  545. }
  546.  
  547. fix_mushroom_hex(x,y)
  548. int x,y;
  549. {
  550.     draw_hex(tmpside,x,y,FALSE);
  551. }
  552.